package com.example.java_gobang.game.model;

import com.example.java_gobang.JavaGobangApplication;
import com.example.java_gobang.game.dto.GameRequest;
import com.example.java_gobang.game.service.OnlineUserManager;
import com.example.java_gobang.game.service.RoomManager;
import com.example.java_gobang.game.vo.GameResponse;
import com.example.java_gobang.model.User;
import com.example.java_gobang.model.UserMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.UUID;

/**
 * 表示一个游戏房间类。
 * 每个房间有一个唯一的房间ID，并且可以容纳两个玩家。
 */
@Getter
@Slf4j
public class Room {
    private static final int MAX_ROW = 15;
    private static final int MAX_COL = 15;

    // 房间的唯一标识符
    private final String roomId;

    // 先手玩家ID
    private Integer whiteUser;

    // 房间中的第一个玩家
    private User user1;

    // 房间中的第二个玩家
    private User user2;

    // 棋盘
    private final int[][] board = new int[MAX_ROW][MAX_COL];

    // 用户管理器
    private final OnlineUserManager onlineUserManager;

    // 房间管理器
    private final RoomManager roomManager;

    private final UserMapper userMapper;

    private final ObjectMapper mapper = new ObjectMapper();

    /**
     * 构造方法，创建一个新的房间，并生成一个唯一的房间ID。
     */
    public Room() {
        // 生成一个唯一的房间ID
        this.roomId = UUID.randomUUID().toString();
        // 获取用户管理器和房间管理器的Bean实例
        onlineUserManager = JavaGobangApplication.applicationContext.getBean(OnlineUserManager.class);
        roomManager = JavaGobangApplication.applicationContext.getBean(RoomManager.class);
        userMapper = JavaGobangApplication.applicationContext.getBean(UserMapper.class);
    }

    /**
     * 向房间里加入用户
     *
     * @param user 要加入房间的用户
     */
    public Boolean addUser(User user) {
        // 如果 user1 为空，将用户设为 user1，并将其设为先手玩家
        if (this.user1 == null) {
            this.user1 = user;
            this.whiteUser = user.getUserId();
            return true;
        }

        // 如果 user2 为空且 user 不等于 user1，将用户设为 user2
        if (this.user2 == null && !this.user1.equals(user)) {
            this.user2 = user;
            return true;
        }
        return false;
    }

    // 处理落子操作
    public void putChess(String reqString) throws IOException {
        // 1.记录当前落子位置
        GameRequest gameRequest = mapper.readValue(reqString, GameRequest.class);
        GameResponse gameResponse = new GameResponse();
        int chess = gameRequest.getUserId() == user1.getUserId() ? 1 : 2;
        int row = gameRequest.getRow();
        int col = gameRequest.getCol();

        // 检查当前位置是否已经有子
        if (board[row][col] != 0) {
            log.warn("当前位置 {},{} 已经有子了！", row, col);
            return;
        }

        // 在棋盘上记录落子
        board[row][col] = chess;
        printBoard();

        // 2.判断胜负
        int winner = checkWinner(row, col, chess);

        // 3.返回响应给房间内所有客户端
        gameResponse.setMessage("putChess");
        gameResponse.setUserId(gameRequest.getUserId());
        gameResponse.setRow(row);
        gameResponse.setCol(col);
        gameResponse.setWinner(winner);

        // 获取房间内玩家的WebSocket会话
        WebSocketSession session1 = onlineUserManager.getFromGameRoom(user1.getUserId());
        WebSocketSession session2 = onlineUserManager.getFromGameRoom(user2.getUserId());

        // 检查玩家是否掉线，并设置胜利者为未掉线的玩家
        if (session1 == null) {
            gameResponse.setWinner(user2.getUserId());
            log.info("玩家 {} 掉线！", user1.getUserId());
        }
        if (session2 == null) {
            gameResponse.setWinner(user1.getUserId());
            log.info("玩家 {} 掉线！", user2.getUserId());
        }

        // 向所有连接的玩家发送消息
        if (session1 != null) {
            session1.sendMessage(new TextMessage(mapper.writeValueAsString(gameResponse)));
        }
        if (session2 != null) {
            session2.sendMessage(new TextMessage(mapper.writeValueAsString(gameResponse)));
        }

        // 4.如果胜负确定则结束游戏
        if (gameResponse.getWinner() != 0) {
            log.info("玩家 {} 获胜！", gameResponse.getWinner());

            int winUserId = gameResponse.getWinner();
            int loseUserId = gameResponse.getWinner() == user1.getUserId() ? user2.getUserId() : user1.getUserId();
            userMapper.userWin(winUserId);
            userMapper.userLose(loseUserId);

            // 将玩家移出房间
            onlineUserManager.exitGameRoom(user1.getUserId());
            onlineUserManager.exitGameRoom(user2.getUserId());

            // 销毁房间
            log.info("即将销毁房间 {} ！", this.roomId);
            roomManager.remove(this, user1.getUserId(), user2.getUserId());
        }
    }

    // 打印当前棋盘状态
    private void printBoard() {
        System.out.println("###########################################");
        for (int i = 0; i < MAX_ROW; i++) {
            for (int j = 0; j < MAX_COL; j++) {
                System.out.print(board[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println("###########################################");
    }

    // 检查当前落子是否形成五子连珠
    private int checkWinner(int row, int col, int chess) {
        // 水平方向
        if (checkDirection(row, col, -1, 0, chess)) {
            log.info("玩家 {} 在位置 ({}, {}) 水平方向五子连珠", chess, row, col);
            return chess;
        }
        // 垂直方向
        if (checkDirection(row, col, 0, 1, chess)) {
            log.info("玩家 {} 在位置 ({}, {}) 垂直方向五子连珠", chess, row, col);
            return chess;
        }
        // 斜向右下
        if (checkDirection(row, col, -1, 1, chess)) {
            log.info("玩家 {} 在位置 ({}, {}) 斜向右下五子连珠", chess, row, col);
            return chess;
        }
        // 斜向左下
        if (checkDirection(row, col, 1, 1, chess)) {
            log.info("玩家 {} 在位置 ({}, {}) 斜向左下五子连珠", chess, row, col);
            return chess;
        }

        return 0;
    }

    // 检查给定方向上是否有五子连珠
    private boolean checkDirection(int row, int col, int dx, int dy, int playerNum) {
        log.info("检查玩家 {} 在位置 ({}, {}) 方向 ({}, {}) 的五子连珠", playerNum, row, col, dx, dy);
        for (int i = 0; i < 5; i++) {
            try {
                if (board[row + (4 * dy) - i * dy][col + (4 * dx) - i * dx] == playerNum
                        && board[row + (3 * dy) - i * dy][col + (3 * dx) - i * dx] == playerNum
                        && board[row + (2 * dy) - i * dy][col + (2 * dx) - i * dx] == playerNum
                        && board[row + (1 * dy) - i * dy][col + (1 * dx) - i * dx] == playerNum
                        && board[row - i * dy][col - i * dx] == playerNum) {
                    log.info("玩家 {} 在位置 ({}, {}) 方向 ({}, {}) 五子连珠成功", playerNum, row, col, dx, dy);
                    return true;
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }

//            System.out.println("########### 检测棋盘 ###########");
//            int[][] arr = new int[MAX_ROW][MAX_COL];
//            for (int a = 0; a < MAX_ROW; a++) {
//                for (int b = 0; b < MAX_COL; b++) {
//                    arr[a][b] = 0;
//                }
//            }
//            arr[row + (4 * dy) - i*dy][col + (4 * dx) - i*dx] = 1;
//            arr[row + (3 * dy) - i*dy][col + (3 * dx) - i*dx] = 1;
//            arr[row + (2 * dy) - i*dy][col + (2 * dx) - i*dx] = 1;
//            arr[row + (1 * dy) - i*dy][col + (1 * dx) - i*dx] = 1;
//            arr[row + (0 * dy) - i*dy][col + (0 * dx) - i*dx] = 1;
//
//            for (int a = 0; a < MAX_ROW; a++) {
//                for (int b = 0; b < MAX_COL; b++) {
//                    if(arr[a][b] == 0)
//                    {
//                        System.out.print("· ");
//                    }
//                    else {
//                        System.out.print("1 ");
//                    }
//                }
//                System.out.println();
//            }
//            System.out.println("###############################");
        }

        return false;
    }

}
